#include <Arduino.h>
#include <DNSServer.h>
#include <ESPmDNS.h>
#include <HardwareSerial.h>
#include <Preferences.h>
#include <String.h>
#include <WebServer.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include "WiFiUdp.h"
#include <Update.h>
#include <WiFi.h>
#include "soc/rtc_wdt.h"
// ------------------------------------------------------------------------------------------
#define RESET_PIN 21
#define STOP_OK_PIN 13
#define JDQ_BAT 25
#define JDQ_YKQ 26
#define JDQ_YKQ_CD 27
#define DJQ_BAT_ON digitalWrite(JDQ_BAT, 1)
#define DJQ_BAT_OFF digitalWrite(JDQ_BAT, 0)
#define DJQ_YKQ_ON digitalWrite(JDQ_YKQ, 1)
#define DJQ_YKQ_OFF digitalWrite(JDQ_YKQ, 0)
#define DJQ_YJQ_CD_ON digitalWrite(JDQ_YKQ_CD, 1)
#define DJQ_YJQ_CD_OFF digitalWrite(JDQ_YKQ_CD, 0)

#define LED_PIN 2
#define LED_ON digitalWrite(LED_PIN, 1)
#define LED_OFF digitalWrite(LED_PIN, 0)


void init_IO() {
  pinMode(JDQ_BAT, OUTPUT);
  pinMode(JDQ_YKQ, OUTPUT);
  pinMode(JDQ_YKQ_CD, OUTPUT);
  pinMode(LED_PIN, OUTPUT);
  pinMode(RESET_PIN, INPUT);
  pinMode(STOP_OK_PIN, INPUT);
  LED_OFF;
  DJQ_BAT_OFF;
  DJQ_YJQ_CD_OFF;
  DJQ_YKQ_OFF;
}
void blinkLED(int led, int n, int t) {
  for (int i = 0; i < 2 * n; i++) {
    LED_ON;
    delay(t);
    LED_OFF;
    delay(t);
  }
}

String wifi_ssid = "tptest";    //暂时存储wifi账号密码
String wifi_pass = "12345678";  //暂时存储wifi账号密码


#define BAT_CLIENT_UDP_ADDR "192.168.1.184"
#define BAT_CLIENT_UDP_PORT 9110
#define CHR_CLIENT_UDP_PORT 9120
#define YKQ_SERVER_TCP_PORT 12300

String indexhtml = String("") + "<!DOCTYPE html>\n" + "<head>\n" + "    <meta charset=\"UTF-8\">\n" + "    <title>Update dev</title>\n" + "</head>\n" + "<body>\n" + "    <form method=\'POST\' action=\'/update\' enctype=\'multipart/form-data\'>\n" + "        <input type=\'file\' name=\'firmware\'>\n" + "        <input type=\'submit\'>\n" + "    </form>\n" + "</body>\n";
bool shouldreboot = false;  // 重启标志，固件升级操作完成后设置该标志准备重启设备

IPAddress local_IP(192, 168, 1, 110);
IPAddress gateway(192, 168, 1, 1);
IPAddress subnet(255, 255, 255, 0);

WebServer server(80);  //开启web服务, 创建TCP SERVER,参数: 端口号,最大连接数
WiFiServer tcpServer;
WiFiUDP udp;

// ------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------
void initWiFi() {
  WiFi.disconnect(true);
  WiFi.mode(WIFI_STA);
  WiFi.setSleep(true);
}
void wifiConnecting() {
  WiFi.setAutoConnect(true);  // 设置自动连接
  WiFi.begin(wifi_ssid.c_str(), wifi_pass.c_str());
  int connectTime = 0;
  while (!WiFi.isConnected()) {  //等待WIFI连接成功, 用于连接计时，如果长时间连接不成功，复位设备
    LED_ON;
    vTaskDelay(pdMS_TO_TICKS(25));
    LED_OFF;
    vTaskDelay(pdMS_TO_TICKS(25));
    if (connectTime++ > 5 * 20) {
      ESP.restart();
    }
  }
  Serial.println("WiFi connect success");
  Serial.print("local IP:");
  Serial.println(WiFi.localIP());
  debugSend("WiFi connect success");
}
void wifiTryConn() {
  if (!WiFi.isConnected()) {
    initWiFi();
    wifiConnecting();
  }
}

void handleResponse_ota()  //回调函数
{
  server.sendHeader("Connection", "close");
  server.send(200, "text/html", (Update.hasError()) ? "FAIL" : "OK");
}

void handleRoot_ota()  //回调函数
{
  String username = "chargebat";
  String userpassword = "123456";
  if (!server.authenticate(username.c_str(), userpassword.c_str()))  //校验用户是否登录
  {
    return server.requestAuthentication();  //请求进行用户登录认证
  }
  server.sendHeader("Connection", "close");
  server.send(200, "text/html", indexhtml);  //登录成功则显示真正的内容
}

void handleFileupload_ota()  //回调函数
{
  HTTPUpload& upload = server.upload();    // 文件上传对象
  if (upload.status == UPLOAD_FILE_START)  // 文件上传开始
  {
    Serial.printf("start upload: %s\n", upload.filename.c_str());
    if (!Update.begin())  // 开始固件升级，检查空可用间大小，如果正在升级或空间不够则返回false
    {
      Update.printError(Serial);
    }
  } else if (upload.status == UPLOAD_FILE_WRITE)  // 文件读写中
  {
    if (Update.write(upload.buf, upload.currentSize) != upload.currentSize)  // 将文件数据写入到OTA区域
    {
      Update.printError(Serial);
    }
  } else if (upload.status == UPLOAD_FILE_END)  // 文件上传完成
  {
    Serial.println("write" + String(upload.totalSize) + "byte to OTA area");
    if (!Update.end(true))  // 完成数据写入，设置在系统重启后自动将OTA区域固件移动到Sketch区域 // Update.begin不指定大小时这里设置true
    {
      Update.printError(Serial);
    }
    Serial.println("finished, reboot and update after 1s");
    shouldreboot = true;
  } else {
    Serial.printf("firmware upload error: status=%d\n", upload.status);
  }
}

void udpSend(String msg) {
  wifiTryConn();
  udp.beginPacket(BAT_CLIENT_UDP_ADDR, BAT_CLIENT_UDP_PORT);
  udp.print(msg);
  udp.endPacket();
}

void debugSend(String msg) {
  wifiTryConn();
  udp.beginPacket("192.168.1.100", 10001);
  udp.print("[charge debug]: " + msg + "\n");
  udp.endPacket();
}

TaskHandle_t tcpServerTask;
TaskHandle_t ledTask;
TaskHandle_t checkTask;
TaskHandle_t YKQTask;
TaskHandle_t xTaskWdg;
// ------------------------------------------------------------------------------------------
void setup() {
  Serial.begin(115200);
  Serial.println("\r\n\r\n\r\n SerialRunning setup");

  init_IO();

  if (!WiFi.config(local_IP, gateway, subnet)) {  //静态IP
    Serial.println("STA Failed to configure");
  };
  String hostName = "flycharge";
  if (!WiFi.setHostname(hostName.c_str())) {
    Serial.println("Hostname failed to configure");
  }
  if (!MDNS.begin(hostName.c_str())) {  // http://flycharge.local
    Serial.println("Error setting up MDNS responder!");
    while (1) {
      delay(9000);
      ESP.restart();
    }
  }


  wifiTryConn();

  server.on("/", handleRoot_ota);                                             //注册链接和回调函数
  server.on("/update", HTTP_POST, handleResponse_ota, handleFileupload_ota);  //绑定回调函数
  server.begin();                                                             //启动服务器

  tcpServer.begin(YKQ_SERVER_TCP_PORT);  //TCP服务器启动 接收遥控器的命令
  udp.begin(CHR_CLIENT_UDP_PORT);        //启动UDP监听电池UDP端口

  xTaskCreatePinnedToCore(xFuncTcpServer, "tcpServerTask", 10000, NULL, 6, &tcpServerTask, 0);
  xTaskCreate(xFuncLed, "ledTask", 10000, NULL, 3, &ledTask);
  xTaskCreate(xFuncCheck, "checkTask", 10000, NULL, 4, &checkTask);
  xTaskCreate(xFuncYKQ, "YQKTask", 10000, NULL, 5, &YKQTask);
  xTaskCreate(wdgTask, "wdgTask", 10000, NULL, 2, &xTaskWdg);
  LED_OFF;
  Serial.println("setup done");
  rtc_wdt_protect_off();
  rtc_wdt_enable();
  rtc_wdt_set_time(RTC_WDT_STAGE0, 8000);


  debugSend("setup done");
}

void wdgTask(void* params) {
  int toggle = 0;
  while (1) {
    if (toggle) {
      LED_ON;
      toggle = 0;
    } else {
      LED_OFF;
      toggle = 1;
    }
    delay(500);
    rtc_wdt_feed();
    wifiTryConn();
  }
}



#define CMD_FLY_START "fly_take_off"
#define CMD_FLY_START_CONFIRM "fly_take_off_ok\n"
#define CMD_FLY_START_FAIL "fly_take_off_error\n"
#define CMD_FLY_LAND "fly_land"
#define CMD_FLY_LAND_CONFIRM "fly_land_ok\n"

#define CMD_CHARGE_ON "charge_on"
#define CMD_CHARGE_OFF "charge_off"
#define CMD_CHARGE_OFF_CONFIRM "charge_off_confirm"

#define CMD_BAT_IS_START "bat_is_start?"
#define CMD_BAT_IS_START_REPLY "bat_start_ok"
#define CMD_BAT_IS_START_OFF "bat_start_off"

#define CMD_BAT_STOP "bat_stop"
#define CMD_BAT_STOP_COMFIRM "bat_stop_ok"

#define CMD_BAT_START_CHARGE "bat_start_charge"
#define CMD_BAT_START_CHARGE_COMFIRM "bat_start_charge_ok"

#define CMD_BAT_CHARGE_FINISHED "bat_charge_finished"

#define CMD_BAT_FLY "bat_start_fly"
#define CMD_BAT_FLY_COMFIRM "bat_start_fly_ok"

volatile int isLandOK = 0;
volatile int isFLyInitOk = 0;
volatile int isYKQStart = 0;
volatile int isYKQTrigger = 0;
volatile int isBatChargeCmdConfirm = 0;
volatile int chargeCntDelay = 0;
volatile int isBatChargeFinished = 0;
volatile int isBatStarted = 0;
volatile int isChargeOffConfirm = 0;

volatile int cnBatCharge = 0;

int Data_length;
String msg = "";
char udpIncomingPacket[255];  //存储Udp客户端发过来的数据
void loop() {
  server.handleClient();  //处理来自客户端的请求 OTA

  /*接收发送过来的udp数据*/
  Data_length = udp.parsePacket();
  if (Data_length) {
    int len = udp.read(udpIncomingPacket, 255);  //读取数据，将数据保存在数组incomingPacket中
    if (len > 0) {                               //为了避免获取的数据后面乱码做的判断
      udpIncomingPacket[len] = 0;
    }
    msg = "";
    msg = String(udpIncomingPacket);
    debugSend(msg);

    if (msg.indexOf(CMD_BAT_FLY_COMFIRM) != -1) {
      DJQ_BAT_OFF;
      isBatStarted = 1;
      isFLyInitOk = 1;
      debugSend("bat power on, fly ready");
    }

    if (msg.indexOf(CMD_BAT_STOP_COMFIRM) != -1) {
      isLandOK = 1;
      DJQ_BAT_OFF;
      isBatStarted = 0;
      isChargeOffConfirm = 1;
      debugSend("bat power off");
    }

    if (msg.indexOf(CMD_BAT_START_CHARGE_COMFIRM) != -1) {
      debugSend("bat is charging");
      isBatChargeCmdConfirm = 1;
    }

    if (msg.indexOf(CMD_BAT_CHARGE_FINISHED) != -1) {
      DJQ_YJQ_CD_OFF;
      isBatChargeFinished = 1;
    }

    if (msg.indexOf(CMD_BAT_IS_START_REPLY) != -1) {
      isBatStarted = 1;
    }

    if (msg.indexOf(CMD_BAT_IS_START_OFF) != -1) {
      isBatStarted = 0;
    }
  }

  if (shouldreboot) {
    delay(1000);
    ESP.restart();
  }

  delay(1);
}

volatile int YKQPowerOncnt = 0;
volatile int isCheckEnable = 1;
volatile int cntPowerOff = 0;


void xFuncCheck(void* params) {
  while (1) {
    //检测是否降落
    if (isCheckEnable) {
      if (!digitalRead(STOP_OK_PIN)) {
        delay(50);  //消抖
        if (!digitalRead(STOP_OK_PIN)) {
          isCheckEnable = 0;
          debugSend("isLand=1");
          udpSend(CMD_BAT_STOP);
          //关闭遥控器
          if (isYKQStart) {
            isYKQTrigger = 1;
          }
        }
      }
    }
    delay(100);
  }
}

void xFuncLed(void* params) {

  while (1) {

    delay(1000);

    if (cntPowerOff) {
      cntPowerOff--;
      DJQ_BAT_OFF;
      DJQ_YJQ_CD_OFF;
      if (cntPowerOff == 0) {
        isChargeOffConfirm = 1;
        if (isYKQStart) { isYKQTrigger = 1; }
      }
    }

    if (YKQPowerOncnt) {
      YKQPowerOncnt--;
      DJQ_YJQ_CD_OFF;
      DJQ_BAT_ON;  //底板上电 给无人机ESP32上电,延时6秒后 通知电池上电，断开底座供电
      if (YKQPowerOncnt == 0) {
        udpSend(CMD_BAT_FLY);
        debugSend(CMD_BAT_FLY);
        if (!isYKQStart) { isYKQTrigger = 1; }
      }
    }

    if (cnBatCharge) {
      cnBatCharge--;
      DJQ_BAT_ON;
      DJQ_YJQ_CD_ON;
      if (cnBatCharge == 0) {
        udpSend(CMD_BAT_START_CHARGE);
        debugSend(CMD_BAT_START_CHARGE);
      }
    }
  }
}

void xFuncYKQ(void* params) {
  while (1) {
    if (isYKQTrigger) {
      isYKQTrigger = 0;
      DJQ_YKQ_ON;
      delay(200);
      DJQ_YKQ_OFF;
      delay(1000);
      DJQ_YKQ_ON;
      delay(2500);
      DJQ_YKQ_OFF;
      isYKQStart = !isYKQStart;
      String status = "";
      if (isYKQStart) {
        status = "on";
      } else {
        status = "off ";
      };
      debugSend("ykq power " + status);
    }
    delay(100);
  }
}

void xFuncTcpServer(void* params) {
  int i = 0;
  while (1) {
    WiFiClient client = tcpServer.available();  //尝试建立客户对象
    if (client) {                               //如果当前客户可用
      Serial.println("[tcp client exist]");
      while (client.connected()) {                     //如果客户端处于连接状态
        if (client.available()) {                      //如果有可读数据
          String line = client.readStringUntil('\n');  //读取一个字节，也可以用readLine()等其他方法
          debugSend(line);                             //从串口打印

          if (line.indexOf(CMD_FLY_START) != -1) {
            client.print("fly_take_off_rcvd\n");
            isCheckEnable = 0;
            YKQPowerOncnt = 6;
          }

          if (line.indexOf(CMD_FLY_LAND) != -1) {
            client.print("fly_land_rcvd\n");            
            isCheckEnable = 1;
          }

          if (line.indexOf(CMD_CHARGE_ON) != -1) {
            client.print("charge_on_rcvd\n");
            isYKQStart = 0;
            isCheckEnable = 0;
            cnBatCharge = 6;  //6秒后 发送电池充电指令
          }

          if (line.indexOf(CMD_CHARGE_OFF) != -1) {
            client.print("charge_off_rcvd\n");
            isCheckEnable = 0;
            cntPowerOff = 6;  //6秒后关闭 遥控器
          }

          if (line.indexOf(CMD_BAT_IS_START) != -1) {
            udpSend(CMD_BAT_IS_START);
            debugSend(CMD_BAT_IS_START);
          }
        }

        if (isChargeOffConfirm) {
          isChargeOffConfirm = 0;
          client.print("bat_charge_off_ok\n");
          debugSend("bat_charge_off_ok\n");
        }

        if (isBatChargeFinished) {
          isBatChargeFinished = 0;
          client.print("bat_charge_finished\n");  //向客户端发送
          debugSend("bat_charge_finished\n");
        }

        if (isFLyInitOk) {
          isFLyInitOk = 0;
          client.print("fly_take_off_ok\n");  //向客户端发送
          debugSend("fly_take_off_ok\n");
        }

        if (isLandOK) {
          isLandOK = 0;
          client.print("fly_land_ok\n");  //向客户端发送
          debugSend("fly_land_ok\n");
        }


        if (isBatChargeCmdConfirm) {
          isBatChargeCmdConfirm = 0;
          client.print("bat_charge_on_ok\n");  //向客户端发送
          debugSend("bat_charge_on_ok\n");
        }


        delay(10);
      }
      client.stop();  //结束当前连接:
      Serial.println("[tcp client exit]");
    }
    delay(10);
  }
}